package ltd.jdsoft.cute.spring.web.exception.handler;

import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

import ltd.jdsoft.cute.api.StatusCodeEnum;
import ltd.jdsoft.cute.api.http.HttpHeaderUtils;
import ltd.jdsoft.cute.api.http.RestResponse;
import ltd.jdsoft.cute.spring.web.exception.BizException;
import ltd.jdsoft.cute.spring.web.exception.NoRightOperationException;
import ltd.jdsoft.cute.spring.web.exception.UnAuthorizedException;
import ltd.jdsoft.cute.spring.web.response.ResponseBuilder;

/**
 * 所有http api的异常统一处理类,将异常信息包装成符合接口规范的数据格式
 * 
 * @author zhengzhq
 *
 */
@ControllerAdvice
public class GlobalApiExceptionHandler {

  private static Logger logger = LoggerFactory.getLogger(GlobalApiExceptionHandler.class);

  @ExceptionHandler(value = {UnAuthorizedException.class})
  @ResponseBody
  public RestResponse handleSessionTimeoutException(UnAuthorizedException ex,
      HttpServletResponse response) {
    return ResponseBuilder.newBuilder(response)
        .setMeta(StatusCodeEnum.UNAUTHORIZED.code(), Errors.UN_AUTHENTICATION).build();
  }

  @ExceptionHandler(value = {NoRightOperationException.class})
  @ResponseBody
  public RestResponse handleNoRightOperateException(NoRightOperationException ex,
      HttpServletResponse response) {
    return ResponseBuilder.newBuilder(response)
        .setMeta(StatusCodeEnum.INTERNAL_BIZ_ERROR.code(), Errors.NOT_RIGHT_OPERATE).build();
  }

  /**
   * 请求的http method不支持
   * 
   * @param ex
   * @param response
   * @return
   */
  @ExceptionHandler(value = {HttpRequestMethodNotSupportedException.class})
  @ResponseBody
  public RestResponse handleMethodNotSupportedException(HttpRequestMethodNotSupportedException ex,
      HttpServletRequest request, HttpServletResponse response) {
    // 记录下异常信息，方便分析哪些接口发生的概率
    logger.warn(getLogWithXRequest("unsupport http method", request), ex);

    return ResponseBuilder.newBuilder(response).setMeta(StatusCodeEnum.INTERNAL_BIZ_ERROR.code(),
        String.format(Errors.UNSUPPORT_HTTP_METHOD, ex.getMethod())).build();
  }

  @ExceptionHandler(value = {BindException.class, MethodArgumentNotValidException.class})
  @ResponseBody
  public RestResponse handleValidateException(Exception ex, HttpServletRequest httpServletRequest,
      HttpServletResponse response) {
    BindingResult bindingResult = null;
    if (ex instanceof BindException) {// get方式校验异常
      bindingResult = ((BindException) ex).getBindingResult();
    } else if (ex instanceof MethodArgumentNotValidException) {// post方式校验异常
      bindingResult = ((MethodArgumentNotValidException) ex).getBindingResult();
    }

    // 日志记录下异常字段信息，方便排查问题
    StringBuilder errorBuilder = new StringBuilder();
    if (bindingResult != null) {
      List<FieldError> fieldErrors = bindingResult.getFieldErrors();
      if (!CollectionUtils.isEmpty(fieldErrors)) {
        for (FieldError fieldError : fieldErrors) {
          if (errorBuilder.length() > 0) {
            errorBuilder.append(";");
          }
          errorBuilder.append(fieldError.getDefaultMessage());
        }
      }
    }

    String errorMessage = errorBuilder.toString();
    logger.warn(getLogWithXRequest("illegal arguments {}", httpServletRequest), errorMessage);

    return ResponseBuilder.newBuilder(response)
        .setMeta(StatusCodeEnum.INTERNAL_BIZ_ERROR.code(), errorMessage).build();
  }

  @ExceptionHandler(value = BizException.class)
  @ResponseBody
  public RestResponse handleRestException(BizException ex, HttpServletRequest httpServletRequest,
      HttpServletResponse response) {
    logger.debug(getLogWithXRequest("biz exception occurs", httpServletRequest),
        ex.getExceptionEnum().getMessage());

    return ResponseBuilder.newBuilder(response)
        .setMeta(ex.getExceptionEnum().getCode(), StringUtils.isEmpty(ex.getExtraErrorMsg())
            ? ex.getExceptionEnum().getMessage() : ex.getExtraErrorMsg())
        .build();
  }

  @ExceptionHandler(value = Exception.class)
  @ResponseBody
  public RestResponse handleUnExpectedException(Exception ex, HttpServletRequest httpServletRequest,
      HttpServletResponse response) {
    logger.error(getLogWithXRequest("internal server error occurs", httpServletRequest), ex);

    return ResponseBuilder.newBuilder(response).setMeta(StatusCodeEnum.INTERNAL_SERVER_ERROR)
        .build();
  }

  /**
   * 日志信息拼接request-with的版本信息
   * 
   * @param message
   * @param httpServletRequest
   * @return
   */
  private String getLogWithXRequest(String message, HttpServletRequest httpServletRequest) {
    String requestWith = getXRequestWith(httpServletRequest);
    if (StringUtils.isEmpty(requestWith)) {
      return message;
    }

    return message + " with client " + requestWith;
  }

  /**
   * 获取客户端的版本信息，方便对问题进行定位
   * 
   * @param httpServletRequest
   * @return
   */
  private String getXRequestWith(HttpServletRequest httpServletRequest) {
    String requestWithValue = httpServletRequest.getHeader(HttpHeaderUtils.REQUEST_WITH);
    if (StringUtils.isEmpty(requestWithValue)) {
      return "";
    }

    return requestWithValue;
  }
}
